个人学习之后对于 Java 类和对象的理解
类是一个模板,它描述一类对象的行为和状态,从类到对象即为实例化,从对象到类为抽象
Java 类
Java 类分为外部类和内部类
类可以看成是创建 Java 对象的模板
eg. 创建一个狗的类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class Dog{
String breed; //品种
int age; //年龄
String color; //颜色
//以下为该 Dog 类的方法
//叫
void barking(){
System.out.println("小狗在叫");
}
//饿
void hungry(){
System.out.println("小狗饿了");
}
//睡觉
void sleeping(){
System.out.println("小狗在睡觉");
}
}
Java 外部类
外部类只有 public 和 default(缺省) 两种修饰
如果一个类声明的时候使用了 public class 进行了声明,则类名称必须与文件名称完全一致
如果一个类声明的时候使用了 class 进行了声明,则作为启动类的名称可以与文件名称不一致,但是执行的时候肯定执行的是生成后的名称
在一个 *.java 文件中,只能有一个 public class 的声明(公共接口),但是允许有多个 class 声明;public 类不是必须的1
2
3
4
5
6
7
8
9//公开可访问,被 public 修饰的类可以被其他包访问
public class Student{
}
//同一个包内可访问,该类不能被其他包访问
class{
}
Java 内部类
在外部类的内部再声明一个类则称为内部类,内部类有:静态内部类、成员内部类、局部内部类、匿名内部类
内部类均可结合变量进行理解
静态内部类
静态内部类可以等同看作静态变量,可以用 public,protected,private 修饰
可以定义静态或者非静态的成员
静态内部类的作用是:可以访问外部类中私有的数据
看到带有 $ 符号的 .class 文件基本是内部类
静态内部类可以直接访问外部类的静态数据,无法直接访问成员
eg.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63/*
* 关于静态内部类
* 1、静态内部类可以等同看作静态变量
*
* 内部类重要的作用:可以访问外部类中私有的数据
*
* 2、静态内部类可以直接访问外部类的静态数据,无法直接访问成员
*/
public class OuterClass {
//静态变量
static String s1 = "A";
//成员变量
private static String s2 = "B";
//静态方法
private static void m1() {
System.out.println("static's m1 method execute!");
}
//成员方法
private void m2() {
System.out.println("m2 method execute!");
}
//静态内部类
//可以用访问控制权限的修饰符修饰
//public,protected,private,缺省
static class InnerClass{
//静态方法
public static void m3() {
System.out.println(s1);
m1();
//System.out.println(s2);
//m2();
}
//成员方法
public void m4() {
System.out.println(s1);
m1();
//System.out.println(s2);
//m2();
}
}
//入口
public static void main(String[] args) {
//执行 m3
OuterClass.InnerClass.m3();
//执行 m4
InnerClass inner = new OuterClass.InnerClass();
inner.m4();
}
}
成员内部类
成员内部类可以等同看作成员变量
区别于局部内部类的地方是定义在方法外
成员内部类中不能有静态声明
成员内部类可以访问外部类所有的数据
eg.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61/*
* 关于成员内部类
*
* 1、成员内部类可以等同看作成员变量
*
* 2、成员内部类中不能有静态声明
*
* 3、成员内部类可以访问外部类所有的数据
*/
public class OuterClass02 {
// 静态变量
static String k1 = "A";
// 成员变量
private static String k2 = "B";
// 静态方法
private static void y1() {
System.out.println("static's y1 method execute!");
}
// 成员方法
private void y2() {
System.out.println("y2 method execute!");
}
// 成员内部类
// 可以用访问控制权限的修饰符修饰
// public,protected,private,缺省
class InnerClass {
// 静态方法
/*
* public static void y3() {
*
* }
*/
// 成员方法
public void y4() {
System.out.println(k1);
y1();
System.out.println(k2);
y2();
}
}
// 入口
public static void main(String[] args) {
// 创建外部类对象
OuterClass02 oc = new OuterClass02();
InnerClass inner = oc.new InnerClass();
inner.y4();
}
}
局部内部类
局部内部类等同于局部变量
区别于成员内部类的地方是定义在方法内
局部内部类不能有静态声明
重点:局部内部类在访问局部变量的时候,局部变量必须使用 final 修饰
eg.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/*
* 局部内部类等同于局部变量
*
* 重点:局部内部类在访问局部变量的时候,局部变量必须使用 final 修饰
*/
public class OuterClass03 {
//方法
public void f1() {
//局部变量
final int i = 10;
//局部内部类
//局部内部类不能用访问控制权限修饰符修饰
class InnerClass{
//内部类不能有静态声明
//public static void m1() {}
//成员方法
public void f2() {
System.out.println(i);//10
}
}
//调用 m2
InnerClass inner = new InnerClass();
inner.f2();
}
//入口
public static void main(String[] args) {
OuterClass03 oc = new OuterClass03();
oc.f1();
}
}
匿名内部类
匿名类不能使用任何关键字和访问控制符
eg.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48/*
* 匿名内部类:指的是类没有名字
*/
public class Test01 {
//静态方法
public static void t(CustomerService cs) {
cs.logout();
}
//入口
public static void main(String[] args) {
//调用 t 方法
//t(new CustomerServiceImpl());
//使用匿名内部类的方式执行 t 方法
//整个这个 "new CustomerService(){}" 就是个匿名内部类
t(new CustomerService() {
public void logout() {
System.out.println("系统已经安全退出!");
}
});
//匿名内部类的优点:少定义一个类
//缺点:无法重复使用!
}
}
//接口
interface CustomerService{
//退出系统
void logout();
}
//不使用匿名内部类的情况
//编写一个类实现 CustomerService 接口
/*
class CustomerServiceImpl implements CustomerService{
public void logout() {
System.out.println("系统已经安全退出!");
}
}
*/
局部、静态、成员内部类的访问区别
eg. 局部内部类的访问1
2
3
4
5
6
7
8
9
10
11
12
13
14//访问局部内部类必须先有外部类对象
public class Outer{
public void f(final int k){
//局部内部类
class Inner{//定义在方法内部
……
}
}
public static void main(String[] args){
//访问局部内部类必须先有外部类对象
Outer out = new Outer();
out.f(2);
}
}
eg. 静态内部类的访问1
2
3
4
5
6
7
8
9
10
11
12
13//外部类访问内部类的非静态成员:实例化内部类即可
public class Outer{
//静态内部类
static class Inner{
static void inner_f1(){
}
}
public void outer_f2{
//访问
Inner inn = new Inner();
inn.inner_f1();
}
}
eg. 成员内部类的访问1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class Outer{
class Inner{
void inner_f1(){
……
}
}
//外部类的非静态方法访问成员内部类
public void outer_f1(){
Inner inn = new Inner();
inn.inner_f1();
}
//外部类的静态方法访问成员内部类
public static void outer_f2(){
//建立外部类对象
Outer out = new Outer();
//根据外部类对象建立内部类对象
Inner inn = out.new Inner();
//访问内部类的方法
inn.inner_f1();
}
}
对象
面向对象三大特征:封装、继承、多态1
1. 封装:问权限控制 public > protected > 缺省 > private 内部类也是一种封装
public 公开,任何位置都可以访问
protected 同包,子类
缺省 同胞
private 私有,只能在本类中访问1
2
32. 继承:一般类只能单继承,内部类实现多继承,接口可以多继承
3. 多态:编译时多态,体现在向上转型和向下转型,通过引用类型判断调用哪个方法(静态分派)。
运行时多态,体现在同名函数通过不同参数实现多种方法(动态分派)。
对象具有状态和行为,从类到对象即为实例化
从类中创造对象:
- 声明:声明一个对象,包括对象名称和对象类型
- 实例化:使用关键字 new 来创建一个对象
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象; eg. Dog gg = new Dog();
eg. 下面创建一个名为 Puppy 的狗对象1
2
3
4
5
6
7
8
9
10public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}
实例化对象后访问实例变量和方法1
2
3
4
5
6//实例化对象
ObjectReference = new Constructor();
//访问类中的变量
ObjectReference.variableName;
//访问类中的方法
ObjectReference.methodName();
eg. 定义一个计算机类和学生类,让其中一个学生去使用其中的一台某品牌的某型号笔记本电脑1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55/*
* 需求:
* 定义一个计算机类(电脑/笔记本)属性:
* - 品牌
* - 型号
* - 颜色
*
* 定义一个学生类属性:
* - 学号
* - 姓名
* - 学生有一台笔记本电脑
*
* 请编写程序来表示以上的类,分别将类创建为对象
* 对象数量不限,然后让其中的一个学生去使用其中的一台笔记本电脑
*
* 编译并运行,并且将整个执行过程采用图形的方式描述出来
*/
public class OOTest06 {
public static void main(String[] args) {
Computer emt = new Computer();
emt.brand = "SummerCommpany";
emt.number = "SUM-002";
Stuu kmt = new Stuu();
kmt.no = 6623798;
kmt.name = "WiHieree";
emt.s = kmt;
kmt.c = emt;
System.out.println(kmt.name + " 使用的电脑是 " + kmt.c.brand + " 旗下的 " + kmt.c.number);
}
}
class Computer{
String brand;
String number;
String col;
Stuu s;
}
class Stuu{
int no;
String name;
Computer c;
}
封装
所有属性私有化,使用 private 关键字进行修饰,private 表示私有的,修饰的所有数据,使得数据只能在本类访问
封装主要是因为 Java 有访问权限的控制 public > protected > package > default > private1
2
封装可以保护类中的信息,只提供想要被外界访问的信息
对外提供简单的操作入口1
2
3
4
5
6
7
8
9
10
111. 对外提供两个公开的方法,分别是 set 方法和 get 方法
2. 想修改 age 属性,调用 set 方法
命名规范:
public void setAge(int a){
}
3. 想读取 age 属性,调用 get 方法
命名规范:
public int getAge(){
return age;
}
eg. 新建一个 User.java ,并进行封装保证输入年龄在合理范围内1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package com.wihieree01;
public class User {
//属性私有化
private int age;
/*
set 方法没有返回值,因为 set 方法只负责修改数据
public void setAge(int age) {
age = age; // Java 有就近原则,这里其实并没有给 age 属性赋值,这里的 age 都是局部变量 age
}
*/
public void setAge(int a) {
//编写业务逻辑代码进行安全控制
//age = a;
if(a <0 || a > 150) {
System.out.println("对不起,您提供的年龄不合法");
return;
}
//程序可以执行到这里,说明 a 年龄是合法的,则进行赋值运算
age = a;
}
//getter
public int getAge() {
return age;
}
}
eg. 新建一个 UserTest.java 创建一个 User 对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package com.wihieree01;
public class UserTest {
public static void main(String[] args) {
//创建 User 对象
User user = new User();
//编译报错,age 属性私有化,在外部程序中不能直接访问
//从此之后 age 属性非常安全,但是有点太安全了
//对于目前的程序来说,age 属性彻底在外部访问不到了
//System.out.println(user.age);
//在 User.Java 添加 setter and getter 方法
//修改
//user.setAge(-100); //对不起,您提供的年龄不合法
user.setAge(23); // 23
//读取
System.out.println(user.getAge());
}
}
构造方法
构造方法的作用:创建对象和在创建对象的同时,初始化实例变量的内存空间
eg. 创建一个银行客户对象,查看户主名称,账号和余额
创建一个 Account.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package com.wihieree02;
//账户类
public class Account {
//户主
private String name;
//账号
private String actno;
//余额
private double balance;
//有参数构造方法
public Account(String name,String actno,double balance){
this.name = name;
this.actno = actno;
this.balance = balance;
}
//封装 get / set
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}创建一个 ConstructorTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.wihieree02;
public class ConstructorTest02 {
public static void main(String[] args) {
//查看访问的是哪个属性按 Ctrl + 鼠标移动到查看的元素上单击
//创建对象
Account act1 = new Account("WiHieree","SUM-0984",1980000.0);
System.out.println("户主:" + act1.getName()); //户主:WiHieree
System.out.println("账号:" + act1.getActno()); //账号:SUM-0984
System.out.println("余额:" + act1.getBalance()); //余额:1980000.0
}
}
继承
继承的基本作用是:代码复用,但是继承最重要的作用是:有了继承才有了以后方法的覆盖和多态机制
Java 中的继承只能单继承,但是可以通过内部类继承其他类来实现多继承
语法结构:1
2
3
4
5
6
7[修饰符列表] class 类名 extends 父类名{//关键字 extends
类体 = 属性 + 方法
}
// 私有的不支持继承
// 构造方法不支持继承
// 其他数据都可以被继承
虽然 java 语言当中只支持单继承,但是一个类也可以间接继承其它类,例如:1
2
3
4
5
6
7C extends B{
}
B extends A{
}
A extends T{
}
// C 直接继承 B 类,但是 C 类间接继承 T、A类
java 中假设一个类没有显示的继承任何类,该类默认继承 JavaSE 库当中提供的 java.lang.Object 类
1 | package com.day1403; |
方法覆盖
1 | 关于 java 语言当中方法的覆盖: |
eg.
新建一个 Animal.java
1
2
3
4
5
6
7
8
9
10package com.day1404;
//动物类
public class Animal {
//动物都是可以移动的
public void move() {
System.out.println("动物在移动");
}
}新建一个 Bird.java 去继承 Animal ,此时鸟儿是在飞,要满足鸟儿飞就需要方法覆盖
1
2
3
4
5
6
7
8
9
10
11package com.day1404;
//飞禽类
public class Bird extends Animal{
//方法覆盖
public void move() {
System.out.println("鸟儿在飞行");
}
}新建一个 Cat.java 去继承 Animal ,此时猫应该是走猫步,满足这个条件需要方法覆盖
1
2
3
4
5
6
7
8
9package com.day1404;
//猫科类
public class Cat extends Animal{
public void move() {
System.out.println("猫在走猫步");
}
}建一个测试 OverrideTest01.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class OverrideTest01 {
public static void main(String[] args) {
//创建动物对象
Animal a = new Animal();
a.move(); //动物在移动
//创建猫科类动物对象
Cat c = new Cat();
c.move(); //猫在走猫步
//创建飞禽类动物对象
Bird b = new Bird(); //鸟儿在飞行
b.move();
}
}
多态
多态语法机制,暂时参考上面的 Animal.java 和 Cat.java
使用多态语法机制:1
2
3
4Animal a1 = new Cat();
//向下类型转换 父类 --> 子类
Cat c1 = (Cat)a1;
//当调用的方法或者访问的属性是子类型中特有的,在父类型当中不存在,必须进行向下转型
eg.
修改 Cat.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.day1405;
//猫类
public class Cat extends Animal{
//重写父类中继承过来的方法
public void move() {
//!!!super 关键字
//若重写后还是想要输出父类 Animal 的 move() 方法
//super.move(); //动物在移动
System.out.println("猫在溜达溜达");
}
//子类对象特有的行为
public void catchMouse() {
System.out.println("猫抓老鼠!");
}
}修改 Bird.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.day1405;
//鸟儿类
public class Bird extends Animal{
//重写
public void move() {
System.out.println("鸟儿在飞翔");
}
public void fly() {
System.out.println("Bird Fly");
}
}新建 Test.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75package com.day1405;
public class Test {
public static void main(String[] args) {
//使用多态语法机制
Animal a1 = new Cat(); // 子类 --> 父类 称为向上转型
a1.move(); //猫在溜达溜达 //结果主要看底层对象,底层对象是 Cat 调用的方法也是 Cat 对象的方法
/*
* 需求:
* 假设想让以上的对象执行 catchMouse() 方法
* a2 是无法直接调用的,因为 a2 类型 Animal,Animal 中没有 catchMouse() 方法
* 我们可以将 a2 强制类型转换为 Cat 类型
* a2 的类型是 Animal(父类),转换成 Cat 类型(子类),被称为向下转型/downcasting/强制类型转换
*
* 注:向下转型也需要两种类型之间必须有继承关系。不然编译器报错,强制类型转换需要加强制类型转换符
*
* 什么时候需要使用向下转型呢
* 当调用的方法或者访问的属性是子类型中特有的,在父类型当中不存在,必须进行向下转型
*
*/
//向下转型
Cat c1 = (Cat)a1;
c1.catchMouse(); //猫抓老鼠!
/*
* long x = 100L;
* int i = (int)i;
*/
Animal a2 = new Bird();
/*
* 1、以下程序编译是没有问题的,因为编译器检查到 a2 的数据类型是 Animal,Animal 和 Cat 之间存在继承关系
* 并且 Animal 是父类型,Cat 是子类型,父类型转换成子类型叫做向下转型,语法合格
*
* 2、程序虽然编译通过了,但是程序在运行阶段会出现异常,因为 JVM 堆内存当中真实存在的对象是 Bird 类型
* Bird 对象无法转换成 Cat 对象,因为两种类型之间不存在任何继承关系,此时出现了著名的异常:
* java.lang.ClassCastException
* 类型转换异常,这种异常总是在"向下转型"的时候会发生
*
*/
//Cat c2 = (Cat)a2; // Bird 和 Cat 不存在继承关系
/**
* 1、以上异常只有在强制类型转换的时候会发生,也就是说"向下转型"存在隐患(编译通过,但是运行错误)
* 2、向上转型只要编译通过,肯定可以运行 eg. Animal a = new Cat();
* 3、向下转型编译通过,运行可能错误 eg. Animal a2 = new Bird(); Cat c2 = (Cat)a2;
* 4、怎么避免向下转型出现的 ClassCastException
* 使用 instanceof 运算符可以避免出现以上的异常
* 5、instanceof 运算符:
* 5.1、语法格式:
* (引用 instanceof 数据类型名)
* 5.2、以上运算符的执行结果类型是布尔类型,结果可能是 true/false
* 5.3、关于运算结果 true/false
* 假设:(a instanceof Animal)
* true :
* a 这个引用指向的对象是一个 Animal 类型
* false:
* a 这个引用指向的对象不是一个 Animal 类型
* 6、Java 规范中要求:在进行强制类型转换之前,建议采用 instanceof 运算符进行判断,避免 ClassCastException 出现
*
*/
if(a2 instanceof Cat) {//a2 是一个 Cat 类型的对象
Cat c2 = (Cat)a2;
c2.catchMouse();
}else if(a2 instanceof Bird) {//a3 是一个 Bird 类型的对象
Bird b2 = (Bird)a2;
b2.fly();
}
}
}
eg. 多态在实际开发中的应用,主人喂养宠物
创建一个 Master.java
1
2
3
4
5
6
7
8
9
10
11package com.day1501;
public class Master{
//Master 主人类面向的是一个抽象的 Pet,不再面向具体的宠物
//提倡:面向抽象编程,不要面向具体编程
//面向抽象编程的好处是,耦合度低,扩展力强
public void feed(Pet pet) {// Pet pet 是一个父类型的引用
pet.eat();
}
}创建一个 Pet.java
1
2
3
4
5
6
7
8
9
10
11package com.day1501;
/*
* 宠物
*/
public class Pet {
//所有的宠物都可以吃东西
public void eat() {
}
}创建一个 Cat.java
1
2
3
4
5
6
7
8
9
10
11
12
13package com.day1501;
/*
* 宠物小猫
*/
public class Cat extends Pet{
//小猫爱吃鱼
public void eat() {
System.out.println("小猫在吃鱼!");
}
}创建一个 Dog.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.day1501;
/**
* 宠物小狗
* @author SUMMER
*
*/
public class Dog extends Pet{
public void eat() {
System.out.println("小狗正在啃骨头!");
}
}创建一个 Test.java 用于测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36package com.day1501;
/*
* 多态在实际开发中的作用,以下主人喂养宠物为例说明多态的作用:
* 1、分析:主人喂养宠物这个场景要实现需要进行类型的抽象:
* - 主人 [类]
* - 主人可以喂养宠物,所以主人有喂养的这个动作
* - 宠物 [类]
* - 宠物可以吃东西,所以宠物有吃东西的这个动作
*
* 2、面向对象编程的核心:定义好类,然后将类实例化为对象,给一个环境驱使一下,
* 让各个对象之间协作起来形成一个系统
*
* 3、多态的作用
* 降低程序的耦合度,提高程序的扩展里
* 能使用多态尽量使用多态
* 父类型引用指向子类型对象
*
* 核心:面向抽象编程,尽量不要面向具体编程
*/
public class Test {
public static void main(String[] args) {
//创建主人对象
Master WiHieree = new Master();
//创建猫对象
Cat beer = new Cat();
//主人喂养猫
WiHieree.feed(beer);
//创建小狗对象
Dog yt = new Dog();
//主人喂小狗
WiHieree.feed(yt);
}
}